package org.casscommunity.sights.client.widget.editor;

import java.util.ArrayList;
import java.util.List;

import org.casscommunity.sights.client.gin.AppGinjectorManager;
import org.casscommunity.sights.client.util.GwtCollectionUtils;
import org.casscommunity.sights.client.widget.Highlightable;
import org.casscommunity.sights.shared.event.ClearViolationsEvent;
import org.casscommunity.sights.shared.event.ConstraintViolationEvent;
import org.casscommunity.sights.shared.event.handler.ClearViolationsEventHandler;
import org.casscommunity.sights.shared.event.handler.ConstraintViolationEventHandler;
import org.casscommunity.sights.shared.validation.ConstraintViolationDto;

import com.google.gwt.core.client.GWT;
import com.google.gwt.editor.client.IsEditor;
import com.google.gwt.editor.client.LeafValueEditor;
import com.google.gwt.event.shared.EventBus;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiChild;
import com.google.gwt.uibinder.client.UiConstructor;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HTMLPanel;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.Widget;

public class EditorDecorator<T> extends Composite implements IsEditor<ValueEditorWithDelegate<T>>, Highlightable {

	private static final String HIGHLIGHT = "highlight";

	interface EditorDecoratorBinder extends UiBinder<Widget, EditorDecorator<?>> {}
	private static EditorDecoratorBinder uiBinder = GWT.create(EditorDecoratorBinder.class);

	@UiField
	SimplePanel contents;

	@UiField
	HTMLPanel errorList;

	private ValueEditorWithDelegate<T> editor;
	private Widget widget;
	private HandlerRegistration violationRegistration;
	private HandlerRegistration clearViolationRegistration;

	@UiConstructor
	public EditorDecorator() {
		initWidget(uiBinder.createAndBindUi(this));
	}
	
	public EditorDecorator(IsEditor<LeafValueEditor<T>> widget) {
		this();
		setValueEditor(widget);
	}
	
	@Override
	protected void onLoad() {
		super.onLoad();
		EventBus eventBus = AppGinjectorManager.getInjector().getEventBus();
		registerViolationsHandler(eventBus);
		registerClearViolationsHandler(eventBus);
	}

	private void registerViolationsHandler(EventBus eventBus) {
		violationRegistration = eventBus.addHandler(ConstraintViolationEvent.getType(), new ConstraintViolationEventHandler(){
			public void processViolations(List<ConstraintViolationDto> violations) {
				clearViolations();
				for (ConstraintViolationDto violation : violations) {
					List<ConstraintViolationDto> myViolations = new ArrayList<ConstraintViolationDto>();
					if (editor.getBeanPath().equals(violation.getPath())) {
						myViolations.add(violation);
					}
					showViolations(myViolations);
				}
			}
		});
	}

	private void registerClearViolationsHandler(EventBus eventBus) {
		clearViolationRegistration = eventBus.addHandler(ClearViolationsEvent.getType(), new ClearViolationsEventHandler() {
			public void handleClearViolations() {
				clearViolations();
			}
		});
	}
	
	@Override
	protected void onUnload() {
		super.onUnload();
		violationRegistration.removeHandler();
		clearViolationRegistration.removeHandler();
	}

	@UiChild(limit = 1, tagname = "value-editor")
	public void setValueEditor(IsEditor<LeafValueEditor<T>> widget) {
		this.widget = (Widget) widget;
		contents.add(this.widget);
		setEditor(ValueEditorWithDelegate.of(widget.asEditor()));
	}
	
	public ValueEditorWithDelegate<T> asEditor() {
		return editor;
	}
	
	@Override
	public Widget asWidget() {
		return widget;
	}

	public void setEditor(ValueEditorWithDelegate<T> editor) {
		this.editor = editor;
	}

	private void showViolations(List<ConstraintViolationDto> violations) {
		if (GwtCollectionUtils.isNotEmpty(violations)) {
			Element list = DOM.createElement("ul");
			for (ConstraintViolationDto violation : violations) {
				Element item = DOM.createElement("li");
				item.setInnerText(violation.getMessage());
				list.appendChild(item);
			}
			errorList.add(new HTML(list.getString()));
		}
	}
	
	private void clearViolations() {
		errorList.clear();
	}

	public void highlight(boolean shouldHighlight) {
		if (shouldHighlight) {
			contents.addStyleName(HIGHLIGHT);
		} else {
			contents.removeStyleName(HIGHLIGHT);
		}
	}
	
}
